package com.epoch.base.export;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

public class XmlExporter extends AbstractTextExporter {
    protected static final String XML_AMP = "&";
    protected static final String XML_LT = "<";
    protected static final String XML_GT = ">";
    protected static final String XML_AMP_ESC = "&amp;";
    protected static final String XML_LT_ESC = "&lt;";
    protected static final String XML_GT_ESC = "&gt;";
    protected static final String XML_SPACE = " ";
    protected static final String XML_SLASH = "/";
    protected static final String XML_EQUAL = "=";
    protected static final String XML_QUOT = "\"";
    protected static final String XML_NUM_PRE = "-";
    protected static final String XML_ESC = "\\";
    private static final String XML_DECLARE = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
    private static final String XML_ROOT = "tabledata";
    private static final String XML_FIELDS = "fields";
    private static final String XML_FIELD = "field";
    private static final String XML_DATA = "data";
    private static final String XML_ROW = "row";
    private static final String XML_COLUMN = "column";
    private static final String XML_ID = "id";

    @Override
    protected void doTextExport(BufferedWriter writer, Collection<?> data, List<HeadMeta> headMetas,
        Map<Integer, IExportConvertor> indexCvtorMap, Map<String, IExportConvertor> fieldCvtorMap,
        Map<String, Object> params) throws IOException, ExportException {
        writer.write(XML_DECLARE);
        writer.write(this.buildXmlTagStart(XML_ROOT));
        if (CollectionUtils.isNotEmpty(headMetas)) {
            writer.write(this.buildXmlTagStart(XML_FIELDS));
            for (HeadMeta meta : headMetas) {
                writer.write(this.buildXmlElement(XML_FIELD, meta.getTitle()));
            }
            writer.write(this.buildXmlTagEnd(XML_FIELDS));
            if (data != null) {
                writer.write(this.buildXmlTagStart(XML_DATA));
                int r = 0;
                for (Object rowData : data) {
                    writer.write(this.buildXmlTagStart(XML_ROW, XML_ID, String.valueOf(r + 1)));
                    if (rowData != null) {
                        int cols = headMetas.size();
                        for (int c = 0; c < cols; c++) {
                            HeadMeta meta = headMetas.get(c);
                            String colTag = XML_COLUMN + XML_NUM_PRE + String.valueOf(c + 1);
                            String field = meta.getField();
                            Object val = this.obtainFieldVal(rowData, indexCvtorMap, fieldCvtorMap, field, r, c);
                            String valStr = null;
                            if (val instanceof Date) {
                                valStr = ExportConstant.DATETIME_FORMAT.format((Date)val);
                            } else if (val != null) {
                                valStr = val.toString();
                            }
                            writer.write(this.buildXmlElement(colTag, valStr));
                        }
                    }
                    writer.write(this.buildXmlTagEnd(XML_ROW));
                    r++;
                }
                writer.write(this.buildXmlTagEnd(XML_DATA));
            }
        }
        writer.write(this.buildXmlTagEnd(XML_ROOT));
    }

    @Override
    public String getFileNameExt() {
        return ".xml";
    }

    protected String buildXmlTagStart(String name, String... propVals) {
        StringBuilder buff = new StringBuilder();
        buff.append(XML_LT);
        buff.append(name);
        int propCnt = propVals.length / 2;
        int i = 0;
        for (; i < propCnt; i++) {
            int propIdx = i * 2;
            String propName = propVals[propIdx];
            String propVal = propVals[propIdx + 1];
            buff.append(XML_SPACE);
            buff.append(propName);
            buff.append(XML_EQUAL);
            buff.append(XML_QUOT);
            buff.append(this.escXmlPropVal(propVal));
            buff.append(XML_QUOT);
        }
        buff.append(XML_GT);
        return buff.toString();
    }

    protected String buildXmlTagEnd(String name) {
        StringBuilder buff = new StringBuilder();
        buff.append(XML_LT);
        buff.append(XML_SLASH);
        buff.append(name);
        buff.append(XML_GT);
        return buff.toString();
    }

    protected String buildXmlElement(String name, String val, String... propVals) {
        StringBuilder buff = new StringBuilder();
        buff.append(this.buildXmlTagStart(name, propVals));
        buff.append(this.escXmlText(val));
        buff.append(this.buildXmlTagEnd(name));
        return buff.toString();
    }

    protected String escXmlPropVal(String val) {
        String result = StringUtils.trimToEmpty(val);
        result = result.replace(XML_ESC, XML_ESC + XML_ESC);
        result = result.replace(XML_QUOT, XML_ESC + XML_QUOT);
        return result;
    }

    protected String escXmlText(String val) {
        String result = StringUtils.trimToEmpty(val);
        result = result.replace(XML_AMP, XML_AMP_ESC);
        result = result.replace(XML_LT, XML_LT_ESC);
        result = result.replace(XML_GT, XML_GT_ESC);
        return result;
    }
}
